package com.example.security;

import ch.qos.logback.classic.encoder.JsonEncoder;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.example.config.SnowflakeConfig;
import com.example.dto.ClientSettingsDTO;
import com.example.dto.Oauth2RegisteredClientDTO;
import com.example.dto.TokenSettingsDTO;
import com.example.feign.UsernameFeignClient;
import com.example.model.api.Result;
import com.example.utils.ConvertUtils;
import com.example.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

@Slf4j
@Service
public class RegisteredClientServiceImpl {

    @Autowired
    private SnowflakeConfig snowflake;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JdbcOperations jdbcTemplate;

    public Oauth2RegisteredClientDTO saveRegisteredClient(Oauth2RegisteredClientDTO dto) {
        // 空值生成id
        String id = StrUtil.isEmpty(dto.getId()) ? Long.toString(snowflake.nextId()) : dto.getId();

        ClientSettingsDTO clientSettingsDTO = dto.getClientSettingsDTO();
        dto.setId(id);
        TokenSettingsDTO tokenSettings1 = dto.getTokenSettings();
        RegisteredClient registeredClient = RegisteredClient.withId(dto.getId())
                .clientId(dto.getClientId())
                .clientName(dto.getClientName())
                .clientSecret(passwordEncoder.encode(dto.getClientSecret()))
                .clientAuthenticationMethods(clientAuthenticationMethodSet -> {
                    Set<ClientAuthenticationMethod> clientAuthenticationMethods = dto.getClientAuthenticationMethods();
                    clientAuthenticationMethodSet.addAll(clientAuthenticationMethods);
                })
                .authorizationGrantTypes(authorizationGrantTypeSet -> {
                    Set<AuthorizationGrantType> authorizationGrantType = dto.getAuthorizationGrantTypes();
                    authorizationGrantTypeSet.addAll(authorizationGrantType);
                })
                .redirectUri(dto.getRedirectUris())
                .postLogoutRedirectUri(dto.getPostLogoutRedirectUris())
                .scopes(scopeSet -> {
                    Set<String> scopes = dto.getScopes();
                    scopeSet.addAll(scopes);
                })
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(clientSettingsDTO.getRequireAuthorizationConsent()).requireProofKey(clientSettingsDTO.getRequireProofKey()).build())
                .tokenSettings(
                        TokenSettings.builder()
                                .accessTokenTimeToLive(tokenSettings1.getAccessTokenTimeToLive()) // 设置访问令牌的有效期为1小时
                                .refreshTokenTimeToLive(tokenSettings1.getRefreshTokenTimeToLive()) // 设置刷新令牌的有效期为7天
                                .reuseRefreshTokens(tokenSettings1.getReuseRefreshTokens())                  // 设置是否重用刷新令牌，默认是true 控制刷新令牌是否可以被重复使用。
                                .idTokenSignatureAlgorithm(tokenSettings1.getIdTokenSignatureAlgorithm()) // 设置ID令牌的签名算法为RS256
                                .accessTokenFormat(tokenSettings1.getAccessTokenFormat())      // 设置访问令牌格式为参考类型（存储在服务器端）
                                .build()
                )
                .build();
        // 保存客户端（新增和修改）
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        registeredClientRepository.save(registeredClient);
        return dto;
    }

    public Oauth2RegisteredClientDTO get(String id) {
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        RegisteredClient byId = registeredClientRepository.findById(id);

        ClientSettings clientSettings = byId.getClientSettings();
        Boolean requireProofKey = (Boolean) clientSettings.getSetting(ConfigurationSettingNames.Client.REQUIRE_PROOF_KEY);
        Boolean requireAuthorizationConsent = (Boolean) clientSettings.getSetting(ConfigurationSettingNames.Client.REQUIRE_AUTHORIZATION_CONSENT);

        ClientSettingsDTO clientSettingsDTO = new ClientSettingsDTO();
        clientSettingsDTO.setRequireProofKey(requireProofKey);
        clientSettingsDTO.setRequireAuthorizationConsent(requireAuthorizationConsent);

        TokenSettings tokenSettings = byId.getTokenSettings();
        TokenSettingsDTO tokenSettingsDTO = new TokenSettingsDTO();
        tokenSettingsDTO.setIdTokenSignatureAlgorithm(tokenSettings.getIdTokenSignatureAlgorithm());
        tokenSettingsDTO.setRefreshTokenTimeToLive(tokenSettings.getRefreshTokenTimeToLive());
        tokenSettingsDTO.setDeviceCodeTimeToLive(tokenSettings.getDeviceCodeTimeToLive());
        tokenSettingsDTO.setAccessTokenFormat(tokenSettings.getAccessTokenFormat());
        tokenSettingsDTO.setAuthorizationCodeTimeToLive(tokenSettings.getAuthorizationCodeTimeToLive());
        tokenSettingsDTO.setReuseRefreshTokens(tokenSettings.isReuseRefreshTokens());
        tokenSettingsDTO.setAccessTokenTimeToLive(tokenSettings.getAccessTokenTimeToLive());


        Oauth2RegisteredClientDTO oauth2RegisteredClientDTO = ConvertUtils.sourceToTarget(byId, Oauth2RegisteredClientDTO.class);
        oauth2RegisteredClientDTO.setClientSettingsDTO(clientSettingsDTO);
        oauth2RegisteredClientDTO.setTokenSettings(tokenSettingsDTO);
        Set<String> redirectUris = byId.getRedirectUris();
        Set<String> postLogoutRedirectUris = byId.getPostLogoutRedirectUris();
        oauth2RegisteredClientDTO.setRedirectUris(ListUtil.toList(redirectUris).get(0));
        oauth2RegisteredClientDTO.setPostLogoutRedirectUris(ListUtil.toList(postLogoutRedirectUris).get(0));
        return oauth2RegisteredClientDTO;

    }
} 